package net.ncraft.mc.easyframework.application;

import net.ncraft.mc.easyframework.LeadLoading;
import net.ncraft.mc.easyframework.application.configuration.ApplicationConfiguration;
import net.ncraft.mc.easyframework.FunctionRegister;
import net.ncraft.mc.easyframework.context.ApplicationContext;
import net.ncraft.mc.easyframework.context.core.DefaultApplicationContext;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;

public class EasyApplication {
    private ApplicationContext context;

    public static void run(EasyApplication application, JavaPlugin plugin) {
        ApplicationConfiguration configuration = new ApplicationConfiguration();
        run(application, plugin, configuration);
    }

    public static void run(EasyApplication application, JavaPlugin plugin, ApplicationConfiguration configuration) {

        application.context = new DefaultApplicationContext();
        application.context.addBean(application.context);
        application.context.addBean(plugin);
        //排除掉framework包
        application.context.scan(plugin.getClass(), "net.ncraft.mc.easyframework");
        //自动装配
        Set<String> loadOrder = new LinkedHashSet<>();
        InputStream boot = application.getClass().getClassLoader().getResourceAsStream("META-INF/easy.boot");
        if (boot != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(boot));
            //循环读每一行的Package Name
            Set<String> packageName = new LinkedHashSet<>();
            String line;
            while (true) {
                try {
                    if ((line = reader.readLine()) == null) break;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                loadOrder.add(line);
            }
        } else {
            throw new RuntimeException("找不到 META-INF/easy.boot 文件");
        }

        Set<String> loadFactories = new LinkedHashSet<>();
        try {

            URL meta = application.getClass().getClassLoader().getResource("META-INF");

            if (meta != null && meta.getProtocol().equals("jar")) {

                String jarPath = meta.getPath().substring(5, meta.getPath().indexOf("!"));
                JarFile jarFile = new JarFile(jarPath);

                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String entryName = entry.getName();
                    // 检查条目名称是否以指定的前缀开始
                    if (entryName.endsWith(".easy") && !entry.isDirectory()) {
                        InputStream easyIn = application.getClass().getClassLoader().getResourceAsStream(entryName);
                        BufferedReader easyReader = new BufferedReader(new InputStreamReader(easyIn));
                        while (easyReader.ready()) {
                            String factories;
                            while (true) {
                                try {
                                    if ((factories = easyReader.readLine()) == null) break;
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                                loadFactories.add(factories);
                            }
                        }
                    }
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //优先按照loadOrder排序
        Set<String> scanPackage = new LinkedHashSet<>();
        scanPackage.addAll(loadOrder);
        scanPackage.addAll(loadFactories);
        for (String s : scanPackage) {
            application.context.scan(s);
        }

        application.context.init();


        for (Class<?> component : application.context.getComponents()) {
            if (LeadLoading.class.isAssignableFrom(component)) {
                LeadLoading leadLoading = (LeadLoading) application.context.getBean(component);
                leadLoading.init();
            }
        }

        application.context.initExplicitBeans();
        application.context.reAutowire();

        for (Class<?> component : application.context.getComponents()) {
            if (FunctionRegister.class.isAssignableFrom(component)) {
                FunctionRegister register = (FunctionRegister) application.context.getBean(component);
                register.register();
            }
        }

        application.context.reAutowire();
    }
}
